﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;
using System.Text.RegularExpressions;

namespace EdiNet
{
    public class EdiXmlWriter : XmlWriter
    {
        enum EdiXmlWriterState
        {
            RootElement,
            SegmentElement,
            DataElement,
            CompositeElement,
            Value
        }

        Regex elementNameRegex = new Regex(@"^([EC])(\d+)$", RegexOptions.Compiled);
        EdiStreamWriter baseWriter;
        Stack<EdiXmlWriterState> internalStateStack = new Stack<EdiXmlWriterState>();
        DataSegment currentSegment;
        DataElement currentDataElement;
        int currentComponentIndex;


        public EdiXmlWriter(Stream ediStream, EdiReaderWriterSettings settings)
            : this(new EdiStreamWriter(ediStream, settings))
        {
        }
        public EdiXmlWriter(EdiStreamWriter ediStreamWriter)
        {
            baseWriter = ediStreamWriter;
            internalStateStack.Push(EdiXmlWriterState.RootElement);
        }

        public override void Close()
        {
            baseWriter.Close();
        }
        #region Not Implemented
        public override string LookupPrefix(string ns)
        {
            throw new NotImplementedException();
        }
        public override void WriteBase64(byte[] buffer, int index, int count)
        {

            throw new NotImplementedException();
        }
        public override void WriteCData(string text)
        {
            throw new NotImplementedException();
        }
        public override void WriteCharEntity(char ch)
        {
            throw new NotImplementedException();
        }
        public override void WriteChars(char[] buffer, int index, int count)
        {
            throw new NotImplementedException();
        }
        public override void WriteComment(string text)
        {
            throw new NotImplementedException();
        }
        public override void WriteDocType(string name, string pubid, string sysid, string subset)
        {
            throw new NotImplementedException();
        }
        public override void WriteEndAttribute()
        {
            throw new NotImplementedException();
        }
        public override void WriteEntityRef(string name)
        {
            throw new NotImplementedException();
        }
        public override void WriteProcessingInstruction(string name, string text)
        {
            throw new NotImplementedException();
        }
        public override void WriteRaw(string data)
        {
            throw new NotImplementedException();
        }
        public override void WriteRaw(char[] buffer, int index, int count)
        {
            throw new NotImplementedException();
        }
        public override void WriteStartAttribute(string prefix, string localName, string ns)
        {
            throw new NotImplementedException();
        }
        public override void WriteSurrogateCharEntity(char lowChar, char highChar)
        {
            throw new NotImplementedException();
        }
        #endregion

        #region ignored
        public override void WriteEndDocument()
        {
        }
        public override void WriteStartDocument(bool standalone)
        {
            throw new NotImplementedException();
        }
        public override void WriteStartDocument()
        {
        }
        public override void Flush()
        {
        }
        #endregion

        public override void WriteEndElement()
        {
            internalStateStack.Pop();
            switch (internalStateStack.Peek())
            {
                case EdiXmlWriterState.SegmentElement:
                    baseWriter.WriteSegment(currentSegment);
                    break;
            }
        }

        public override void WriteFullEndElement()
        {
            WriteEndElement();
        }

        public override void WriteStartElement(string prefix, string localName, string ns)
        {
            EdiXmlWriterState currentState = internalStateStack.Peek();
            switch (currentState)
            {
                case EdiXmlWriterState.RootElement:
                    internalStateStack.Push(EdiXmlWriterState.SegmentElement);
                    break;
                case EdiXmlWriterState.SegmentElement:
                    internalStateStack.Push(EdiXmlWriterState.DataElement);
                    currentSegment = new DataSegment(localName);
                    break;
                case EdiXmlWriterState.DataElement:
                case EdiXmlWriterState.CompositeElement:
                    Match m = elementNameRegex.Match(localName);
                    if (!m.Success)
                        throw new InvalidOperationException("Invalid data element name: " + localName);
                    int elementIndex = int.Parse(m.Groups[2].Value);
                    if (m.Groups[1].Value == "E")
                    {
                        if (currentState == EdiXmlWriterState.DataElement)
                        {
                            currentDataElement = currentSegment[elementIndex];
                            currentComponentIndex = 0;
                        }
                        else
                        {
                            currentComponentIndex = elementIndex - 1;
                        }
                        internalStateStack.Push(EdiXmlWriterState.Value);
                    }
                    else
                    {
                        if (currentState == EdiXmlWriterState.CompositeElement)
                            throw new InvalidOperationException("Nested composite? " + localName);
                        internalStateStack.Push(EdiXmlWriterState.CompositeElement);
                        currentDataElement = currentSegment[elementIndex];
                    }
                    break;
                default:
                    throw new InvalidOperationException("Unexpected element:" + localName);
            }
        }

        public override WriteState WriteState
        {
            get
            {

                switch (internalStateStack.Peek())
                {
                    case EdiXmlWriterState.RootElement:
                    case EdiXmlWriterState.SegmentElement:
                    case EdiXmlWriterState.DataElement:
                    case EdiXmlWriterState.CompositeElement:
                        return WriteState.Element;
                    case EdiXmlWriterState.Value:
                        return WriteState.Content;
                    default:
                        throw new NotImplementedException(internalStateStack.Peek().ToString());
                }
            }
        }

        public override void WriteString(string text)
        {
            switch (internalStateStack.Peek())
            {
                case EdiXmlWriterState.Value:
                    currentDataElement[currentComponentIndex] += text;
                    break;
                default:
                    throw new InvalidOperationException("Unexpected string:" + text);
            }
        }
        public override void WriteWhitespace(string ws)
        {
            WriteString(ws);
        }
    }
}
